#!/usr/bin/env python3
"""
FastAPI推荐接口实现
集成ES、Redis缓存、MySQL数据源
"""

from fastapi import APIRouter, Depends, HTTPException, Query, Path
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy.orm import Session
from typing import List, Dict, Any, Optional
from pydantic import BaseModel
import asyncio
from datetime import datetime

from models import get_db, User, UserTag, Tag, Content, ContentLocation
from services.es_client import ESClient
from services.recommendation_cache import RecommendationCache, CacheStrategy
from services.auth_service import jwt_auth
from config.redis import RedisKeys, RedisUtils

# 创建路由器
router = APIRouter()
security = HTTPBearer()

# 请求/响应模型
class RecommendationRequest(BaseModel):
    """推荐请求模型"""
    page: int = 1
    size: int = 20
    use_cache: bool = True
    tag_id: Optional[int] = None  # 可选的标签ID，用于频道过滤

class RecommendationResponse(BaseModel):
    """推荐响应模型"""
    success: bool
    message: str
    data: Optional[Dict[str, Any]] = None
    total: int = 0
    page: int = 1
    size: int = 20

class ContentRecommendation(BaseModel):
    """内容推荐模型"""
    content_id: int
    title: str
    description: str
    author_nickname: str
    tag_names: List[str]
    first_image_url: Optional[str]
    like_count: int
    comment_count: int
    view_count: int
    published_at: str
    score: float

class TextSimilarityRequest(BaseModel):
    """文本相似推荐请求"""
    content_id: int
    size: int = 20
    min_score: float = 0.1

class LocationRecommendationRequest(BaseModel):
    """位置推荐请求"""
    city: Optional[str] = None
    latitude: Optional[float] = None
    longitude: Optional[float] = None
    radius: float = 10000  # 半径（米）
    page: int = 1
    size: int = 20

# 依赖注入：获取当前用户
async def get_current_user(
    credentials: HTTPAuthorizationCredentials = Depends(security),
    db: Session = Depends(get_db)
) -> User:
    """获取当前登录用户"""
    token = credentials.credentials
    
    # 验证Token
    payload = jwt_auth.verify_token(token)
    if not payload:
        raise HTTPException(
            status_code=401,
            detail="Token无效或已过期"
        )
    
    user_id = payload.get("user_id")
    if not user_id:
        raise HTTPException(
            status_code=401,
            detail="Token中缺少用户信息"
        )
    
    # 检查Redis中的Token是否匹配
    redis_token_key = RedisKeys.USER_TOKEN.format(user_id=user_id)
    stored_token = RedisUtils.get(redis_token_key)
    if stored_token != token:
        raise HTTPException(
            status_code=401,
            detail="Token已失效，请重新登录"
        )
    
    # 从数据库获取用户信息
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        raise HTTPException(
            status_code=404,
            detail="用户不存在"
        )
    
    return user

# 推荐服务类
class RecommendationService:
    """推荐服务"""
    
    def __init__(self):
        self.es_client = ESClient(use_async=True)
        self.cache_manager = RecommendationCache()
    
    async def get_user_tag_weights(self, user_id: int, db: Session) -> Dict[int, float]:
        """获取用户标签权重"""
        try:
            # 先从缓存获取
            cached_weights = await self.cache_manager.get_tag_weights(user_id)
            if cached_weights:
                return cached_weights
            
            # 从数据库获取用户标签
            user_tags = db.query(UserTag).filter(UserTag.user_id == user_id).all()
            
            if not user_tags:
                return {}
            
            # 计算标签权重（基于用户行为，这里简化处理）
            tag_weights = {}
            for user_tag in user_tags:
                # 简化权重计算：可以根据用户点击、浏览等行为调整
                tag_weights[user_tag.tag_id] = 1.0
            
            # 缓存标签权重
            await self.cache_manager.cache_tag_weights(user_id, tag_weights)
            
            return tag_weights
            
        except Exception as e:
            print(f"获取用户标签权重失败: {e}")
            return {}
    
    async def get_user_interests(self, user_id: int, db: Session) -> List[int]:
        """获取用户兴趣标签ID列表"""
        try:
            user_tags = db.query(UserTag).filter(UserTag.user_id == user_id).all()
            return [ut.tag_id for ut in user_tags]
            
        except Exception as e:
            print(f"获取用户兴趣标签失败: {e}")
            return []
    
    async def format_recommendations(self, recommendations: List[Dict[str, Any]]) -> List[ContentRecommendation]:
        """格式化推荐结果"""
        formatted_recs = []
        
        for rec in recommendations:
            try:
                formatted_rec = ContentRecommendation(
                    content_id=rec.get("content_id", 0),
                    title=rec.get("title", ""),
                    description=rec.get("description", ""),
                    author_nickname=rec.get("author_nickname", ""),
                    tag_names=rec.get("tag_names", []),
                    first_image_url=rec.get("first_image_url"),
                    like_count=rec.get("like_count", 0),
                    comment_count=rec.get("comment_count", 0),
                    view_count=rec.get("view_count", 0),
                    published_at=rec.get("published_at", ""),
                    score=rec.get("_score", 0.0)
                )
                formatted_recs.append(formatted_rec)
                
            except Exception as e:
                print(f"格式化推荐结果失败: {e}")
                continue
        
        return formatted_recs

# 创建推荐服务实例
recommendation_service = RecommendationService()

@router.post("/recommendations", response_model=RecommendationResponse)
async def get_recommendations(
    request: RecommendationRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """获取个性化推荐内容"""
    try:
        user_id = current_user.id
        page = request.page
        size = request.size
        use_cache = request.use_cache
        
        # 检查缓存
        if use_cache and CacheStrategy.RECOMMENDATION_CACHE["enabled"]:
            cached_recommendations = await recommendation_service.cache_manager.get_user_recommendations(
                user_id, page
            )
            if cached_recommendations:
                formatted_recs = await recommendation_service.format_recommendations(cached_recommendations)
                return RecommendationResponse(
                    success=True,
                    message="推荐获取成功（缓存）",
                    data={
                        "recommendations": [rec.dict() for rec in formatted_recs],
                        "page": page,
                        "size": size
                    },
                    total=len(formatted_recs),
                    page=page,
                    size=size
                )
        
        # 获取用户兴趣标签
        user_tag_ids = await recommendation_service.get_user_interests(user_id, db)
        
        # 如果指定了频道（tag_id），优先使用该频道
        if request.tag_id:
            user_tag_ids = [request.tag_id]
        elif not user_tag_ids:
            return RecommendationResponse(
                success=False,
                message="用户未选择兴趣标签，请先选择兴趣标签",
                data={"recommendations": []},
                total=0,
                page=page,
                size=size
            )
        
        # 获取标签权重
        tag_weights = await recommendation_service.get_user_tag_weights(user_id, db)
        
        # 计算分页参数
        from_ = (page - 1) * size
        
        # 直接从MySQL获取推荐内容（降级方案）
        recommendations = []
        
        try:
            # 获取用户相关的标签对应的内容
            from models import Content, ContentTag, Tag, ContentMedia
            
            # 查询包含用户感兴趣标签的内容
            contents = db.query(Content).join(
                ContentTag, Content.id == ContentTag.content_id
            ).join(
                Tag, ContentTag.tag_id == Tag.id
            ).filter(
                Tag.id.in_(user_tag_ids),
                Content.status == 2  # 已发布
            ).distinct().offset(from_).limit(size).all()
            
            # 格式化推荐结果
            for content in contents:
                # 获取内容的标签
                content_tags = db.query(Tag).join(
                    ContentTag, ContentTag.tag_id == Tag.id
                ).filter(
                    ContentTag.content_id == content.id
                ).all()
                
                # 获取作者信息
                author = db.query(User).filter(User.id == content.author_id).first()
                
                # 获取内容的第一张图片
                first_image = db.query(ContentMedia).filter(
                    ContentMedia.content_id == content.id,
                    ContentMedia.media_type == 'image'
                ).order_by(ContentMedia.sort_order).first()
                
                doc = {
                    "content_id": content.id,
                    "title": content.title or "",
                    "description": content.description or "",
                    "author_nickname": author.nickname if author else "未知",
                    "tag_names": [tag.name for tag in content_tags],
                    "first_image_url": first_image.media_url if first_image else None,
                    "like_count": content.like_count or 0,
                    "comment_count": content.comment_count or 0,
                    "view_count": content.view_count or 0,
                    "published_at": content.published_at.isoformat() if content.published_at else content.created_at.isoformat(),
                    "_score": 1.0  # 默认分数
                }
                recommendations.append(doc)
            
            print(f"MySQL查询返回 {len(recommendations)} 条推荐结果")
            
        except Exception as error:
            print(f"MySQL查询失败: {error}")
            import traceback
            traceback.print_exc()
            recommendations = []
        
        # 格式化推荐结果
        formatted_recs = await recommendation_service.format_recommendations(recommendations)
        
        # 缓存推荐结果
        if use_cache and CacheStrategy.RECOMMENDATION_CACHE["enabled"]:
            await recommendation_service.cache_manager.cache_user_recommendations(
                user_id, page, recommendations
            )
        
        return RecommendationResponse(
            success=True,
            message="推荐获取成功",
            data={
                "recommendations": [rec.dict() for rec in formatted_recs],
                "page": page,
                "size": size
            },
            total=len(formatted_recs),
            page=page,
            size=size
        )
        
    except Exception as e:
        print(f"获取推荐失败: {e}")
        return RecommendationResponse(
            success=False,
            message=f"获取推荐失败: {str(e)}",
            data={"recommendations": []},
            total=0,
            page=request.page,
            size=request.size
        )

@router.post("/recommendations/similar", response_model=RecommendationResponse)
async def get_similar_recommendations(
    request: TextSimilarityRequest,
    current_user: User = Depends(get_current_user)
):
    """获取文本相似推荐"""
    try:
        content_id = request.content_id
        size = request.size
        min_score = request.min_score
        
        # 调用ES文本相似推荐
        recommendations = await recommendation_service.es_client.text_similarity_recommendation(
            content_id=content_id,
            fields=["title", "description"],
            size=size,
            min_score=min_score
        )
        
        # 格式化推荐结果
        formatted_recs = await recommendation_service.format_recommendations(recommendations)
        
        return RecommendationResponse(
            success=True,
            message="相似推荐获取成功",
            data={
                "recommendations": [rec.dict() for rec in formatted_recs],
                "reference_content_id": content_id
            },
            total=len(formatted_recs),
            page=1,
            size=size
        )
        
    except Exception as e:
        print(f"获取相似推荐失败: {e}")
        return RecommendationResponse(
            success=False,
            message=f"获取相似推荐失败: {str(e)}",
            data={"recommendations": []},
            total=0,
            page=1,
            size=request.size
        )

@router.get("/recommendations/hot/{tag_id}", response_model=RecommendationResponse)
async def get_hot_content_by_tag(
    tag_id: int = Path(..., description="标签ID"),
    size: int = Query(20, description="返回数量"),
    current_user: User = Depends(get_current_user)
):
    """获取标签热门内容"""
    try:
        # 检查缓存
        if CacheStrategy.HOT_CONTENT_CACHE["enabled"]:
            cached_hot_contents = await recommendation_service.cache_manager.get_hot_content_by_tag(tag_id)
            if cached_hot_contents:
                formatted_recs = await recommendation_service.format_recommendations(cached_hot_contents[:size])
                return RecommendationResponse(
                    success=True,
                    message="热门内容获取成功（缓存）",
                    data={
                        "recommendations": [rec.dict() for rec in formatted_recs],
                        "tag_id": tag_id
                    },
                    total=len(formatted_recs),
                    page=1,
                    size=size
                )
        
        # 调用ES获取热门内容
        recommendations = await recommendation_service.es_client.tag_based_recommendation(
            user_tag_ids=[tag_id],
            size=size
        )
        
        # 格式化推荐结果
        formatted_recs = await recommendation_service.format_recommendations(recommendations)
        
        # 缓存热门内容
        if CacheStrategy.HOT_CONTENT_CACHE["enabled"]:
            await recommendation_service.cache_manager.cache_hot_content_by_tag(
                tag_id, recommendations
            )
        
        return RecommendationResponse(
            success=True,
            message="热门内容获取成功",
            data={
                "recommendations": [rec.dict() for rec in formatted_recs],
                "tag_id": tag_id
            },
            total=len(formatted_recs),
            page=1,
            size=size
        )
        
    except Exception as e:
        print(f"获取热门内容失败: {e}")
        return RecommendationResponse(
            success=False,
            message=f"获取热门内容失败: {str(e)}",
            data={"recommendations": []},
            total=0,
            page=1,
            size=size
        )

@router.post("/recommendations/behavior")
async def record_user_behavior(
    content_id: int = Query(..., description="内容ID"),
    behavior_type: str = Query(..., description="行为类型：view/like/comment/share"),
    current_user: User = Depends(get_current_user)
):
    """记录用户行为"""
    try:
        user_id = current_user.id
        
        # 记录行为到缓存
        await recommendation_service.cache_manager.cache_user_behavior(
            user_id=user_id,
            content_id=content_id,
            behavior_type=behavior_type
        )
        
        return {
            "success": True,
            "message": "用户行为记录成功",
            "data": {
                "user_id": user_id,
                "content_id": content_id,
                "behavior_type": behavior_type,
                "timestamp": datetime.utcnow().isoformat()
            }
        }
        
    except Exception as e:
        print(f"记录用户行为失败: {e}")
        return {
            "success": False,
            "message": f"记录用户行为失败: {str(e)}"
        }

@router.delete("/recommendations/cache")
async def clear_recommendation_cache(
    current_user: User = Depends(get_current_user)
):
    """清除用户推荐缓存"""
    try:
        user_id = current_user.id
        
        # 清除用户相关缓存
        await recommendation_service.cache_manager.clear_user_cache(user_id)
        
        return {
            "success": True,
            "message": "推荐缓存清除成功",
            "data": {
                "user_id": user_id,
                "timestamp": datetime.utcnow().isoformat()
            }
        }
        
    except Exception as e:
        print(f"清除推荐缓存失败: {e}")
        return {
            "success": False,
            "message": f"清除推荐缓存失败: {str(e)}"
        }

@router.get("/recommendations/stats")
async def get_recommendation_stats(
    current_user: User = Depends(get_current_user)
):
    """获取推荐统计信息"""
    try:
        # 获取缓存统计
        cache_stats = await recommendation_service.cache_manager.get_cache_stats()
        
        # ES健康检查
        es_health = await recommendation_service.es_client.health_check()
        
        return {
            "success": True,
            "message": "统计信息获取成功",
            "data": {
                "cache_stats": cache_stats,
                "es_health": es_health,
                "user_id": current_user.id,
                "timestamp": datetime.utcnow().isoformat()
            }
        }
        
    except Exception as e:
        print(f"获取统计信息失败: {e}")
        return {
            "success": False,
            "message": f"获取统计信息失败: {str(e)}"
        }

@router.post("/recommendations/location", response_model=RecommendationResponse)
async def get_location_recommendations(
    request: LocationRecommendationRequest,
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """基于位置的推荐内容"""
    try:
        page = request.page
        size = request.size
        
        # 构建查询条件
        query_filters = []
        
        if request.city:
            # 按城市筛选
            query_filters.append({"term": {"city": request.city}})
        
        if request.latitude and request.longitude:
            # 按地理位置筛选（圆形范围）
            query_filters.append({
                "geo_distance": {
                    "distance": f"{request.radius}m",
                    "location": {
                        "lat": request.latitude,
                        "lon": request.longitude
                    }
                }
            })
        
        # 只返回已发布的内容
        query_filters.append({"term": {"status": 2}})
        
        # 构建ES查询
        search_query = {
            "bool": {
                "must": query_filters
            }
        }
        
        # 计算分页
        from_ = (page - 1) * size
        
        # 执行查询
        search_body = {
            "query": search_query,
            "size": size,
            "from": from_,
            "sort": [{"created_at": {"order": "desc"}}]
        }
        
        # 执行ES查询
        if recommendation_service.es_client.use_async:
            response = await recommendation_service.es_client.es.search(
                index="content_recommend",
                body=search_body
            )
        else:
            response = recommendation_service.es_client.es.search(
                index="content_recommend",
                body=search_body
            )
        
        # 提取结果
        hits = response.get("hits", {}).get("hits", [])
        results = [hit.get("_source", {}) | {"_score": hit.get("_score", 0)} for hit in hits]
        
        # 格式化结果
        formatted_recs = await recommendation_service.format_recommendations(results)
        
        return RecommendationResponse(
            success=True,
            message="位置推荐获取成功",
            data={
                "recommendations": [rec.dict() for rec in formatted_recs],
                "location": {
                    "city": request.city,
                    "latitude": request.latitude,
                    "longitude": request.longitude,
                    "radius": request.radius
                }
            },
            total=len(formatted_recs),
            page=page,
            size=size
        )
        
    except Exception as e:
        print(f"获取位置推荐失败: {e}")
        return RecommendationResponse(
            success=False,
            message=f"获取位置推荐失败: {str(e)}",
            data={"recommendations": []},
            total=0,
            page=request.page,
            size=request.size
        )

@router.get("/locations/cities")
async def get_available_cities(
    current_user: User = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """获取所有可用的城市列表"""
    try:
        # 从数据库获取所有有内容的城市
        cities = db.query(ContentLocation.city).distinct().all()
        city_list = [city[0] for city in cities if city[0]]
        
        return {
            "success": True,
            "message": "城市列表获取成功",
            "data": {
                "cities": city_list,
                "total": len(city_list)
            }
        }
        
    except Exception as e:
        print(f"获取城市列表失败: {e}")
        return {
            "success": False,
            "message": f"获取城市列表失败: {str(e)}",
            "data": {"cities": []}
        }

# 注册路由
app_router = router
